package Navi_run;

import com.alibaba.fastjson.JSON;
import encryption.Hash;
import info.debatty.java.lsh.LSHMinHash;
import it.unisa.dia.gas.jpbc.*;
import it.unisa.dia.gas.plaf.jpbc.pairing.PairingFactory;
import it.unisa.dia.gas.plaf.jpbc.pairing.a.TypeACurveGenerator;

import org.ujmp.core.DenseMatrix;
import org.ujmp.core.Matrix;
import utils.Out;


import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.*;

import static encryption.Opaak.gen_pq;
import static encryption.RSA.generateRsaKeyPair;
import static parameter.parameter_length.*;

public class initialize {
    public static Field GT;
    public static Field G1;
    public static Field G2;
    public static Field Zr;
    public static Pairing pairing;

    public static BigInteger gsk_a;
    public static BigInteger gsk_b;
    public static Element gpk_g;
    public static Element gpk_g2;//就是文章中上边有个小尖角的g
    public static Element gpk_A;
    public static Element gpk_B;
//    public static BigInteger pubPara_r;
//    public static Element pubPara_g;

    public static LSHMinHash lshMinHash;
    public static Matrix HKP;
    public static Matrix SK1;
    public static Matrix SK2;
    public static Matrix V;

    public static BigInteger ZKPK_rou;
    public static BigInteger ZKPK_F;
    public static BigInteger ZKPK_g;
    public static BigInteger ZKPK_b;

    public static BigInteger clSign_PK_n;
    public static BigInteger clSign_PK_d1;
    public static BigInteger clSign_PK_d2;
    public static BigInteger clSign_PK_d3;
    public static BigInteger clSign_SK_p;


    public initialize() {
        long startTime = System.nanoTime();
        params.pairing = pairing = PairingFactory.getPairing("./config/a.properties");
        params.G1 = G1 = pairing.getG1();
        params.G2 = G2 = pairing.getG2();
        params.GT = GT = pairing.getGT();
        params.Zr = Zr = pairing.getZr();
        params.MC = 10;
        //初始化LSH算法
        params.lshMinHash = lshMinHash = new LSHMinHash(band_count, bucket_count, dictionary_count);
        //初始化组公私钥 gpk和gsk
        generateGroupKeyPair();
        generateClSignKeyPair();
        generateSearchKey(row, column);
        getZKPKParameters(qlength);
        long endTime = System.nanoTime();

        System.out.println("初始化花费时间:" + (endTime - startTime));

    }

    public void getPairingParameter() {
        long startTime = System.currentTimeMillis();
        // 初始化pairing曲线
        TypeACurveGenerator typeACurveGenerator = new TypeACurveGenerator(512, 512);//rBit是Zp中阶数p的比特长度；qBit是G中阶数的比特长度
        // 初始化pairing参数
        PairingParameters pairingParameters = typeACurveGenerator.generate();
        // 将pairing参数写入到文件中
        Out out = new Out("./config/a.properties");
        out.println(pairingParameters);
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }

    //TA获取组公私钥
    public void generateGroupKeyPair() {
        try {
            //采用SHA1PRNG的随机算法生成随机数 SHA1PRNG的效率比NativePRNG的效率高
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            //获取组私钥gsk (a,b)来自于正整数群
            params.gsk_a = gsk_a =new BigInteger(gsk_ab_length,secureRandom);
            params.gsk_b = gsk_b =new BigInteger(gsk_ab_length,secureRandom);
            //System.out.println("gsk_a:" + gsk_a + "\ngsk_b:" + gsk_b);
            //获取组公钥gpk
            params.gpk_g = gpk_g = G2.newRandomElement().getImmutable();
            params.gpk_A = gpk_A = gpk_g.pow(gsk_a).getImmutable();
            params.gpk_B = gpk_B = gpk_g.pow(gsk_b).getImmutable();
            params.gpk_g2 = gpk_g2 = G1.newRandomElement().getImmutable();
            // 获取RSA公私钥
            params.RSAkeyMap = generateRsaKeyPair();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    /**
     * CL聚合签名方案的一个验证
     */
    public void clSignature() {
        // 生成对M的签名 私钥为(gsk_a,gsk_b)，公钥为(p,G1,G2,e,gpk_g,GT,gpk_A,gpk_B)
        BigInteger M = new BigInteger("1111111");
        Element A = G2.newRandomElement().getImmutable();
        Element B = A.pow(gsk_b);
        Element C = A.pow(gsk_a).mul(B.pow(gsk_a.multiply(M)));

        System.out.println(A);
        //验证
        System.out.println(pairing.pairing(A, gpk_B));
        System.out.println(pairing.pairing(B, gpk_g));
        if (pairing.pairing(A, gpk_B).isEqual(pairing.pairing(B, gpk_g)) &&
                pairing.pairing(C, gpk_g).isEqual(pairing.pairing(A, gpk_A).mul(pairing.pairing(B, gpk_A).pow(M)))) {
            System.out.println("good");
        } else {
            System.out.println("bad");
        }
    }

    // 生成模糊搜索密钥SK(SK1,SK2,V)
    public static Matrix generateSearchKey(int k1, int k2) {
        Random random = new Random();
        // 生成SK1和SK2
        SK1 = DenseMatrix.Factory.zeros(k1, k2);
        SK2 = DenseMatrix.Factory.zeros(k1, k2);
        for (int i = 0; i < k1; i++) {
            for (int j = 0; j < k2; j++) {
                SK1.setAsBoolean(random.nextBoolean(), i, j);
                SK2.setAsBoolean(random.nextBoolean(), i, j);
            }
        }
        V = DenseMatrix.Factory.rand(1, k2);
        for (int i = 0; i < k2; i++) {
            V.setAsBoolean(random.nextBoolean(), 0, i);
        }
        HKP = DenseMatrix.Factory.zeros(k1, k2);
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < column; j++) {
                HKP.setAsBoolean(random.nextBoolean(), i, j);
            }
        }
        params.SK1 = SK1;
        params.SK2 = SK2;
        params.V = V;
        params.HKP = HKP;
        return null;
    }

    //生成零知识证明要用的参数
    public static boolean getZKPKParameters(int qlength) {
        //    long startTime = System.nanoTime();
        Map<String, BigInteger> pq = gen_pq(qlength);
        BigInteger rou = pq.get("rou");
        BigInteger F = pq.get("F");
        params.ZKPK_rou = ZKPK_rou = rou;
        params.ZKPK_F = ZKPK_F = F;
        BigInteger g = null;
        SecureRandom secureRandom = new SecureRandom();
        BigInteger gTemp = new BigInteger(10, secureRandom);
        BigInteger b = F.subtract(BigInteger.ONE).divide(rou);
        if (gTemp.modPow(b, F).compareTo(BigInteger.ZERO) != 0) {
            g = gTemp.modPow(b, F);
            if (g.modPow(rou, F).compareTo(BigInteger.ONE) == 0) {
                params.ZKPK_g = ZKPK_g = g;
                params.ZKPK_b = ZKPK_b = b;
            } else {
                params.ZKPK_g = ZKPK_g = g;
                params.ZKPK_b = ZKPK_b = b;
                System.out.println("生成零知识证明参数失败");
                return false;
            }
        }
        //    long endTime = System.nanoTime();
        //   System.out.println("生成零知识证明Opaak的参数耗时：" + (endTime - startTime));
        return true;
    }
    //生成Cl-signature要用到的公私钥
    public static Map<String, BigInteger> generateClSignKeyPair() {
        SecureRandom secureRandom = new SecureRandom();
        //生成两个随机大素数pq pq是素数的概率为 1-(1/2)^100
        BigInteger q = new BigInteger(pq_length, 100, secureRandom);
        BigInteger p = new BigInteger(pq_length, 100, secureRandom);
        //n = p*q
        BigInteger n = p.multiply(q);
        //二次剩余 x^2=a mod(n) 有解 a就是n的二次剩余 随机选择x 计算n的二次剩余d1 d2 d3
        BigInteger x = new BigInteger(pq_length, secureRandom);
        BigInteger d1 = x.modPow(BigInteger.TWO, n);
        x = new BigInteger(pq_length, secureRandom);
        BigInteger d2 = x.modPow(BigInteger.TWO, n);
        x = new BigInteger(pq_length, secureRandom);
        BigInteger d3 = x.modPow(BigInteger.TWO, n);
        //输出公私钥 q可以用n除一下得到
        //将公私钥存储
        Map<String, BigInteger> clKeyMap = new HashMap<>();
        params.clSign_PK_n = clSign_PK_n = n;
        params.clSign_PK_d1 = clSign_PK_d1 = d1;
        params.clSign_PK_d2 = clSign_PK_d2 = d2;
        params.clSign_PK_d3 = clSign_PK_d3 = d3;
        params.clSign_SK_p = clSign_SK_p = p;
        return clKeyMap;
    }

    public static void main(String[] args) {
        initialize initialize = new initialize();
    }

}
